package com.atguigu.spzx.manager.interceptor;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.util.AuthContextUtil;
import com.atguigu.spzx.common.exception.GuiguException;
import com.atguigu.spzx.manager.constant.CacheConstant;
import com.atguigu.spzx.model.entity.system.SysUser;
import com.atguigu.spzx.model.vo.common.Result;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

/**
 * 登录校验拦截器：判断用户访问系统功能是否登录。不登录拒绝访问。
 */
@Component
public class LoginAuthInterceptor implements HandlerInterceptor {

    @Autowired
    RedisTemplate<String,String> redisTemplate ;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        StringBuffer requestURL = request.getRequestURL();
        System.out.println("requestURL = " + requestURL);

        //1.跨域预检请求直接放行，无需校验token
        String method = request.getMethod();
        if("OPTIONS".equalsIgnoreCase(method)){
            return true;
        }

        //2.获取请求头是否携带token,不携带token拒绝访问。返回208，需要登录
        String token = request.getHeader("token");
        if(StrUtil.isEmpty(token)){
            //responseNoLoginInfo(response);
            //return false;
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }

        //3.携带token,从redis获取用户信息，用户信息获取不到拒绝访问。208，需要登录
        String sysUserJsonStr = redisTemplate.opsForValue().get(CacheConstant.USER_LOGIN_PREFIX + token);
        if(StrUtil.isEmpty(sysUserJsonStr)){ //令牌错误或缓存失效
            //responseNoLoginInfo(response);
            //return false;
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }

        //4.将用户信息与当前线程进行绑定。便于后续流程从当前线程上获取共享的数据。
        SysUser sysUser = JSON.parseObject(sysUserJsonStr, SysUser.class);
        AuthContextUtil.set(sysUser);

        //5.刷新登录有效时间 TODO 临时有效期
        redisTemplate.expire(CacheConstant.USER_LOGIN_PREFIX + token,3000, TimeUnit.MINUTES);

        return true; //放行，执行controller
    }

    //负责：响应错误结果给前端
    private static void responseNoLoginInfo(HttpServletResponse response) {
        Result result = Result.build(null, ResultCodeEnum.LOGIN_AUTH);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.print(JSON.toJSONString(result));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(writer!=null){
                writer.close();
            }
        }
    }

//    @Override
//    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
//    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清理线程绑定的共享数据：如果不清理，当并发量大，线程多，占用更多内存，导致内存不够用。
        AuthContextUtil.remove();
    }
}
